package com.s3u4.demo.zookeeper.lock;

import com.s3u4.demo.zookeeper.ZkConfig;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * Created by Captain on 01/12/2017.
 */
public class ZkLock implements Watcher {

    private String root = "/zk_lock";

    private ZooKeeper zooKeeper;

    /** 当前 node **/
    private String currentNode;

    /** 等待的node **/
    private String waitNode;

    /** 获取锁标记 **/
    private CountDownLatch execute;

    public ZkLock() {
        try {
            this.zooKeeper = new ZooKeeper(ZkConfig.ZK_HOST, ZkConfig.SESSION_TIMECOUNT,this);
            this.execute = new CountDownLatch(1);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void lock(){
        try {
            Stat stat = this.zooKeeper.exists(root,false);
            if ( stat == null ){
                synchronized (ZkLock.class){
                    if ( this.zooKeeper.exists(root,false) == null ){
                        String path = this.zooKeeper.create(root,null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                        System.out.println("Create root path : " + path);
                    }
                }
            }
            String path = this.zooKeeper.create(root+"/_lock",null,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
            this.currentNode = path.substring(path.lastIndexOf("/") + 1);
            this.checkLocks();
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            // 获取锁前阻塞等待
            execute.await();
            System.out.println("当前锁是 " + this.currentNode + " , 已经获取锁啦~~~~~");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void checkLocks() {
        try {
            List<String> children = this.zooKeeper.getChildren(root,null);
            Collections.sort(children);
            int index = children.indexOf(this.currentNode);
            if ( index == 0 ){
                execute.countDown();
            } else if ( index > 0 ){
                this.waitNode = children.get(index - 1);
                if ( zooKeeper.exists(root + "/" + waitNode,false) == null ){
                    checkLocks();
                    return;
                }
                zooKeeper.getData(root + "/" + waitNode, true, new Stat());
                System.out.println("当前锁是 " + this.currentNode + ", 等待锁 " + this.waitNode);
            } else {
                System.out.println("异常情况出现了。。。");
            }

        } catch (KeeperException e) {
            checkLocks();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void unLock(){
        try {
            System.out.println("执行完毕,删除锁 " + this.currentNode);
            this.zooKeeper.delete(root+"/"+currentNode,-1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        } finally {
            this.close();
        }
    }

    private void close(){
        if ( this.zooKeeper != null ){
            try {
                this.zooKeeper.close();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void process(WatchedEvent event) {
        System.out.println("当前节点状态变更 "+ this.currentNode + " , event Status : " + event.getType());
        if ( event.getType() == Event.EventType.NodeDeleted ) {
            this.checkLocks();
        }
    }

}
